﻿/* HEADER
 * ------
 * © 2009 by Salomon Zwecker 
 * modified by:
 * - 
 */
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

namespace Shapes.Geometry
{
    /// <summary>
    /// a polygon shape
    /// </summary>
    public sealed class Polygon : Shape, ILinedShape
    {
        internal LineStrip _LineStrip;
        /// <summary>
        /// The LineStrip which defines the polygon
        /// </summary>
        public LineStrip LineStrip { get { return _LineStrip; } }

        Line[] _SortedLines;
        /// <summary>
        /// creates a polygon shape
        /// </summary>
        /// <param name="lines">the LineStrip which defines the polygon. 
        /// Note: the linestrip is changed by the polygon. 
        /// Changing the LineStrip later may cause unexpected results.</param>
        public Polygon(LineStrip lines)
            : base()
        {
            _LineStrip = lines;
            _LineStrip.Close();
            _BorderLength = _LineStrip.BorderLength;

            SortLinesByY();

            _Transform = _LineStrip._Transform;

            _LineStrip.OnChangeGeometry += new Action<Drawing>(Lines_OnChangeGeometry);
        }

        void Lines_OnChangeGeometry(Drawing obj)
        {
            //_LineStrip.Close();
            _BorderLength = _LineStrip.BorderLength;

            SortLinesByY();
            CallOnChange();
        }
        internal void SortLinesByY()
        {

            if (_SortedLines != null)
                foreach (Line l in _SortedLines)
                    l.Dispose();
            
            _SortedLines = new Line[_LineStrip._Lines.Count];

            // compare Start- and EndPoint's Y value of each line
            for (int i = 0; i < _SortedLines.Length; i++)
            {
                _SortedLines[i] = (Line)_LineStrip._Lines[i].Clone();

                if (_SortedLines[i].StartPoint.Y > _SortedLines[i].EndPoint.Y)
                {
                    Vector2 tmp                 = _SortedLines[i]._StartPoint;
                    _SortedLines[i]._StartPoint = _SortedLines[i]._EndPoint;
                    _SortedLines[i]._EndPoint   = tmp;
                }
            }

            // compare all lines
            for (int i = 0; i < _SortedLines.Length - 1; i++)
            {
                for (int k = i + 1; k < _SortedLines.Length; k++)
                {
                    if (_SortedLines[k].StartPoint.Y < _SortedLines[i].StartPoint.Y)
                    {
                        Line tmp = _SortedLines[i];
                        _SortedLines[i] = _SortedLines[k];
                        _SortedLines[k] = tmp;
                    }
                }
            }
        }

        internal override bool IsPointInside(ref Vector2 point)
        {
            // scanline test
            Vector2 delta;
            int counter = 0;
            foreach (Line l in _SortedLines)
            {
                // test if the points Y pos is between the vertical position of the lines corners
                if (point.Y >= l.StartPoint.Y)
                {
                    if ((point.Y < l.EndPoint.Y)
                        && (point.X <= Math.Max(l.StartPoint.X, l.EndPoint.X))  // and one corner is on the right hand side
                        && (l.StartPoint.Y != l.EndPoint.Y))                    // and the line is not horizontal
                    {
                        // calculate X coordinate of the intersection point between scanline and line
                        delta.X = l.EndPoint.X - l.StartPoint.X;
                        delta.Y = l.EndPoint.Y - l.StartPoint.Y;
                        float offset = (point.Y - l.StartPoint.Y) * (delta.X / delta.Y) + l.StartPoint.X;

                        // count the intersecting lines
                        if (point.X <= offset)
                            counter++;
                    }
                }
            }

            // if an even amount of lines are crossed, the point must be outside
            if (counter % 2 == 0)
                return false;

            return true;

        }

        internal override float GetDistanceToEdge(ref Vector2 point)
        {
            return _LineStrip.GetDistanceToEdge(ref point);
        }
        internal override Vector2 GetNearestPointOnEdge(ref Vector2 point)
        {
            return _LineStrip.GetNearestPointOnEdge(ref point);
        }

        internal override Vector2 GetPositionFromT(float t)
        {
            return _LineStrip.GetPositionFromT(t);
        }

        internal override float GetEdgePathValueFromPoint(ref Vector2 point)
        {
            return _LineStrip.GetEdgePathValueFromPoint(ref point);
        }
        internal override Vector2 GetTangent(ref Vector2 position)
        {
            return _LineStrip.GetTangent(ref position);
        }
        internal override Vector2 GetNormal(ref Vector2 position)
        {
            return _LineStrip.GetNormal(ref position);
        }
      

        /// <summary>
        /// Clones the Polygon.
        /// </summary>
        /// <returns>an object with the type Polygon</returns>
        public override object Clone()
        {
            Polygon tmp = new Polygon((LineStrip)_LineStrip.Clone());
            tmp.Position = Position;
            tmp.Origin = Origin;
            tmp.Rotation = Rotation;
            tmp.Scale = Scale;
            return tmp;
        }

        /// <summary>
        /// Releases all references inside of the class.
        /// </summary>
        public override void Dispose()
        {
            _LineStrip.Dispose();
            _LineStrip = null;
            base.Dispose();
        }

        /// <summary>
        /// Gets the enumeration type which is mapped to this kind of object
        /// </summary>
        /// <returns>the geometry typee of this object</returns>
        public override GeometryType GetGeometryType()
        {
            return GeometryType.Polygon;
        }

        /// <summary>
        /// Iterates through all Lines of the object
        /// </summary>
        /// <returns>every line the object contains</returns>
        public IEnumerable<Line> GetLines()
        {
            return LineStrip.GetLines();
        }
        /// <summary>
        /// The global position of the first lines start point
        /// </summary>
        public Vector2 StartPoint
        {
            get { return Transform.TransformLocalToGlobal(LineStrip.StartPoint); }
        }

        /// <summary>
        /// The amount of Lines describig the polygon
        /// </summary>
        public int LineCount
        {
            get { return _LineStrip._Lines.Count; }
        }

        /// <summary>
        /// Clones the LineStrip of the Polygon
        /// </summary>
        /// <returns>a line strip object</returns>
        public override LineStrip ToLineStrip()
        {
            return (LineStrip)LineStrip.Clone();
        }
    }
}
